package edu.uiuc.ncsa.commons.util;

import edu.uiuc.ncsa.commons.NCSAKMLUtilityException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author : Sachith Dhanushka (sachith.dhanushka@gmail.com)
 * @date : @date : Jun 7, 2008 - 11:19:27 PM
 */
public class Util {

    static final int BUFFER = 2048;

// Parses an XML file and returns a DOM document.

    // If validating is true, the contents is validated against the DTD
    // specified in the file.

    public static Document parseXmlFile(String filename) throws NCSAKMLUtilityException {
        try {
            // Create a builder factory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // Create the builder and parse the file
            return factory.newDocumentBuilder().parse(new File(filename));
        } catch (SAXException e) {
            // A parsing error occurred; the xml input is not valid
            throw new NCSAKMLUtilityException(e);
        } catch (ParserConfigurationException e) {
            throw new NCSAKMLUtilityException(e);
        } catch (IOException e) {
            throw new NCSAKMLUtilityException(e);
        }
    }

    public static Document parseXmlFile(File file) throws NCSAKMLUtilityException {
        try {
            // Create a builder factory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // Create the builder and parse the file
            return factory.newDocumentBuilder().parse(file);
        } catch (SAXException e) {
            // A parsing error occurred; the xml input is not valid
            throw new NCSAKMLUtilityException(e);
        } catch (ParserConfigurationException e) {
            throw new NCSAKMLUtilityException(e);
        } catch (IOException e) {
            throw new NCSAKMLUtilityException(e);
        }
    }

    public static Document parseXmlFile(InputStream inStream) throws NCSAKMLUtilityException {
        try {
            // Create a builder factory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // Create the builder and parse the file
            return factory.newDocumentBuilder().parse(inStream);
        } catch (SAXException e) {
            // A parsing error occurred; the xml input is not valid
            throw new NCSAKMLUtilityException(e);
        } catch (ParserConfigurationException e) {
            throw new NCSAKMLUtilityException(e);
        } catch (IOException e) {
            throw new NCSAKMLUtilityException(e);
        }
    }

    public static void convertKML20ToKML22(File source, File target) throws NCSAKMLUtilityException {


        try {
            Document sourceDocument = parseXmlFile(source);
            Element sourceDocElement = sourceDocument.getDocumentElement();

            // first change namespace
            sourceDocElement.removeAttribute("xmlns");
            sourceDocElement.setAttribute("xmlns", "http://www.opengis.net/kml/2.2");

            // now change styles, if any
            String xpath = "//kml/Document/Folder/Placemark/Style";

            Element kmlDocument = (Element) sourceDocElement.getElementsByTagName("Document").item(0);

            NodeList folderElements = kmlDocument.getElementsByTagName("Folder");

            Map<String, String> styleMap = new HashMap<String, String>();
            String styleId = "";

            for (int i = 0; i < folderElements.getLength(); i++) {
                Element folderElement = (Element) folderElements.item(i);
                NodeList placemarkElements = folderElement.getElementsByTagName("Placemark");
                for (int j = 0; j < placemarkElements.getLength(); j++) {
                    Element placeMarkElement = (Element) placemarkElements.item(j);
                    NodeList styleElements = placeMarkElement.getElementsByTagName("Style");
                    for (int k = 0; k < styleElements.getLength(); k++) {
                        Element styleElement = (Element) styleElements.item(k);

                        String styleElementStr = serializeToString(styleElement);
                        if (styleMap.containsKey(styleElementStr)) {
                            styleId = styleMap.get(styleElementStr);
                            placeMarkElement.removeChild(styleElement);
                        } else {
                            styleId = "styleId" + (k + 1);
                            styleMap.put(styleElementStr, styleId);

                            // append this after the name element of Folder element

                            NodeList nameElementList = folderElement.getElementsByTagName("name");
                            if (nameElementList.getLength() == 0) {
                                // insert as first child
                                folderElement.insertBefore(styleElement, folderElement.getFirstChild());
                            } else {
                                // after name element
                                folderElement.insertBefore(styleElement, nameElementList.item(0).getNextSibling());
                            }
                        }

                        styleElement.setAttribute("id", styleId);

                        Element styleUrlElement = sourceDocument.createElement("styleUrl");
                        styleUrlElement.setTextContent("#" + styleId);

                        placeMarkElement.insertBefore(styleUrlElement, placeMarkElement.getElementsByTagName("description").item(0).getNextSibling());
                    }
                }
            }

            serializeToFile(sourceDocument, target);

        } catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }


        System.out.println("");
    }

    public static File[] getSourceFiles(String srcArg, String basePath)
            throws IOException, FileNotFoundException {

        //get absolute path

        srcArg = FilenameUtils.concat(basePath, srcArg);
        System.out.println("Path for src files: " + srcArg);

        //verify directory path exists

        String dirPath = FilenameUtils.getFullPathNoEndSeparator(srcArg);
        File dir = new File(dirPath);
        if (!dir.exists() || !dir.isDirectory()) {
            throw new IOException("Source file directory doesn't exist: " + dirPath);
        }

        String filename = FilenameUtils.getName(srcArg);

        //assume filename is a filter
        FileFilter fileFilter = new WildcardFileFilter(filename);

        File[] far = dir.listFiles(fileFilter);
        if (far.length == 0) {
            System.out.println("No files found in directory: " + dirPath + " matching " + filename);
            throw new FileNotFoundException();
        }

        for (File f : far) {
            System.out.println("   " + f.getAbsolutePath());
        }

        return far;

    }

    public static File getDestinationDir(String destArg, String basePath)
            throws IOException, FileNotFoundException {

        destArg = FilenameUtils.concat(basePath, destArg);

        //verify directory path exists

        File dir = new File(destArg);
        if (!dir.exists() || !dir.isDirectory()) {
            throw new FileNotFoundException("Source file directory doesn't exist: " + destArg);
        }

        return dir;
    }

    public static void serializeToFile(Document doc, File file) {
        try {
            // Prepare the DOM document for writing
            Source source = new DOMSource(doc);

            // Prepare the output file
            Result result = new StreamResult(file);

            // Write the DOM document to the file
            Transformer xformer = TransformerFactory.newInstance().newTransformer();
            xformer.transform(source, result);
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }
    }

    public static String serializeToString(Node doc) {
        try {
            // Prepare the DOM document for writing
            Source source = new DOMSource(doc);

            // Prepare the output file
            StringWriter writer = new StringWriter();
            Result result = new StreamResult(writer);

            // Write the DOM document to the file
            Transformer xformer = TransformerFactory.newInstance().newTransformer();


            xformer.transform(source, result);

            return writer.toString();
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void download(String address, File localFile) {
        OutputStream out = null;
        URLConnection conn = null;
        InputStream in = null;
        try {
            URL url = new URL(address);
            out = new BufferedOutputStream(
                    new FileOutputStream(localFile));
            conn = url.openConnection();
            in = conn.getInputStream();
            byte[] buffer = new byte[1024];
            int numRead;
            long numWritten = 0;
            while ((numRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, numRead);
                numWritten += numRead;
            }
            System.out.println(localFile + "\t" + numWritten);
        } catch (Exception exception) {
            exception.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException ioe) {
            }
        }
    }

    public static void unzip(File sourceFile, File targetFolder) {
        try {

            if (!targetFolder.isDirectory()) {
                targetFolder.mkdir();
            }

            BufferedOutputStream dest = null;
            BufferedInputStream is = null;
            ZipEntry entry;
            ZipFile zipfile = new ZipFile(sourceFile);
            Enumeration e = zipfile.entries();
            while (e.hasMoreElements()) {
                entry = (ZipEntry) e.nextElement();

                // this zip file might contain folder underneath
                File extractedLocation = new File(targetFolder, entry.getName()).getParentFile();
                if (!extractedLocation.isDirectory()) {
                    extractedLocation.mkdir();
                }


                is = new BufferedInputStream
                        (zipfile.getInputStream(entry));
                int count;
                byte data[] = new byte[BUFFER];


                String entryName = entry.getName();
                File entryFile = new File(targetFolder, entryName);

                if (entry.isDirectory()) {
                    entryFile.mkdir();
                } else {

                    FileOutputStream fos = new
                            FileOutputStream(entryFile);

                    dest = new
                            BufferedOutputStream(fos, BUFFER);
                    while ((count = is.read(data, 0, BUFFER))
                            != -1) {
                        dest.write(data, 0, count);
                    }
                    dest.flush();
                    dest.close();
                    is.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        try {
            Util.unzip(new File("/tmp/saltstrm.zip"), new File("/tmp/ncsa-util"));
        } catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

    }


}
